//! Ethernet controller configuration validation tests

use axplat_aarch64_opi5p::ethernet::{
    GmacId, GmacLinkSpeed, GmacPhyInterface, ethernet_controller,
};

const SYS_GRF_GMAC_CON8: usize = 0x0320;
const SYS_GRF_GMAC_CON9: usize = 0x0324;
const PHP_GRF_GMAC_CON0: usize = 0x0008;
const PHP_GRF_CLK_CON1: usize = 0x0070;

/// Run GMAC configuration tests mirroring the U-Boot setup sequence.
pub fn run_ethernet_config_tests() {
    info!("=== Starting Ethernet GMAC configuration tests ===");

    let controller = ethernet_controller();

    let mut failures = 0usize;

    // Verify delay registers for both GMAC instances (expected tx=0x30, rx=0x10)
    let delay0 = controller.read_sys_grf(SYS_GRF_GMAC_CON8) & 0xFFFF;
    let delay1 = controller.read_sys_grf(SYS_GRF_GMAC_CON9) & 0xFFFF;
    if delay0 == 0x1030 {
        info!("GMAC0 delay configuration OK: 0x{:04x}", delay0);
    } else {
        error!(
            "GMAC0 delay configuration mismatch: 0x{:04x}, expected 0x1030",
            delay0
        );
        failures += 1;
    }
    if delay1 == 0x1030 {
        info!("GMAC1 delay configuration OK: 0x{:04x}", delay1);
    } else {
        error!(
            "GMAC1 delay configuration mismatch: 0x{:04x}, expected 0x1030",
            delay1
        );
        failures += 1;
    }

    // Verify PHY interface selection bits (RGMII) for both controllers
    let phy_sel = controller.read_php_grf(PHP_GRF_GMAC_CON0);
    const GMAC0_RGMII_MASK: u32 = (1 << 3) | (1 << 4) | (1 << 5);
    const GMAC1_RGMII_MASK: u32 = (1 << 9) | (1 << 10) | (1 << 11);

    if (phy_sel & GMAC0_RGMII_MASK) == (1 << 3) {
        info!("GMAC0 PHY mode set to RGMII");
    } else {
        error!(
            "GMAC0 PHY mode bits unexpected: 0x{:08x}",
            phy_sel & GMAC0_RGMII_MASK
        );
        failures += 1;
    }

    if (phy_sel & GMAC1_RGMII_MASK) == (1 << 9) {
        info!("GMAC1 PHY mode set to RGMII");
    } else {
        error!(
            "GMAC1 PHY mode bits unexpected: 0x{:08x}",
            phy_sel & GMAC1_RGMII_MASK
        );
        failures += 1;
    }

    // Verify link speed divisors switch appropriately between 1GbE and 100MbE
    let check_speed = |speed: GmacLinkSpeed, expected_bits: (u32, u32)| {
        let (bits_to_set, bits_to_clear) = expected_bits;
        controller
            .set_link_speed(GmacId::Gmac0, GmacPhyInterface::Rgmii, speed)
            .unwrap();
        controller
            .set_link_speed(GmacId::Gmac1, GmacPhyInterface::Rgmii, speed)
            .unwrap();
        let clk = controller.read_php_grf(PHP_GRF_CLK_CON1);
        verify_bits(clk, bits_to_set, bits_to_clear)
    };

    let mut speed_failures = 0usize;

    speed_failures += check_speed(
        GmacLinkSpeed::Speed100,
        (
            (1 << 2) | (1 << 3) | (1 << 7) | (1 << 8),
            (1 << 0) | (1 << 5),
        ),
    );

    speed_failures += check_speed(
        GmacLinkSpeed::Speed1000,
        (
            0,
            (1 << 2) | (1 << 3) | (1 << 7) | (1 << 8) | (1 << 0) | (1 << 5),
        ),
    );

    failures += speed_failures;

    if failures == 0 {
        info!("=== Ethernet GMAC configuration tests PASSED ===");
    } else {
        error!(
            "=== Ethernet GMAC configuration tests FAILED ({} issues) ===",
            failures
        );
        panic!("Ethernet GMAC configuration tests failed");
    }
}

fn verify_bits(clk_val: u32, bits_to_set: u32, bits_to_clear: u32) -> usize {
    let mut failures = 0usize;

    if bits_to_set != 0 && (clk_val & bits_to_set) != bits_to_set {
        error!(
            "Clock register bits not set as expected: got 0x{:08x}, expected set bits 0x{:08x}",
            clk_val & bits_to_set,
            bits_to_set
        );
        failures += 1;
    }

    if bits_to_clear != 0 && (clk_val & bits_to_clear) != 0 {
        error!(
            "Clock register bits not cleared as expected: got 0x{:08x}, expected clear mask \
             0x{:08x}",
            clk_val & bits_to_clear,
            bits_to_clear
        );
        failures += 1;
    }

    if failures == 0 {
        info!("Clock register verification passed: 0x{:08x}", clk_val);
    }

    failures
}
